<template>
  <div class="chat-container">
    <div class="chat-header">
      How can I help you today ? ✨
    </div>
    <div ref="chatMessage" class="chat-message">
      <TransitionGroup name="list" tag="ul">
        <component :is="message.role === 'assistant' ? RobotMessage : UserMessage" v-for="(message, index) in messages"
          :key="index" :content="message.content" />
      </TransitionGroup>
    </div>
    <div :class="{ 'is-focus': isFocus }" class="send-chat">
      <textarea v-model="inputValue" placeholder="询问任何问题" @blur="isFocus = false" @focus="isFocus = true"
        @keyup.enter="addUserMessage(inputValue)" />
      <div class="send-btn">
        <button @click="addUserMessage(inputValue)">
          <i class="icon-fasong iconfont"></i>
        </button>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, Ref, nextTick } from "vue";
import RobotMessage from "./RobotMessage/index.vue";
import UserMessage from "./UserMessage/index.vue";

const messages = ref([
  {
    role: "assistant",
    content:
      `你好！我是你的智能助手 🤖有什么可以帮你的吗？ 无论是**技术问题**、**创意点子**，还是**日常小烦**恼，我都乐意帮忙！🎯`,
  },
]);
const inputValue: Ref<string> = ref("");
const loading: Ref<boolean> = ref(true);
const chatMessage: Ref<HTMLDivElement | null> = ref(null);
const isFocus: Ref<boolean> = ref(false);

// 添加机器人消息
function addRobotMessage(message: string) {
  messages.value.push({ role: "assistant", content: message });
}

/**
 * 添加消息到消息列表
 * @param message 要添加的消息
 */
function addUserMessage(message: string) {
  if (message && loading.value) {
    messages.value.push({ role: "user", content: message });
    inputValue.value = "";
    handleScrollTop();
    getMessage(message);
  }
}

// 回到底部
function handleScrollTop() {
  setTimeout(() => {
    if (chatMessage.value) {
      chatMessage.value.scrollTop = chatMessage.value.scrollHeight;
    }
  }, 100);
}

/**
 * 模拟打字机效果，将字符串逐步添加到目标字符串中。
 * @param result 要逐步添加的字符串
 * @param interval 每个字符添加的间隔时间（毫秒），默认为 20 毫秒
 */
const typeWriterEffect = (
  result: string,
  interval: number = 20,
): Promise<void> => {
  return new Promise<void>((resolve) => {
    let index = 0;
    const timer = setInterval(() => {
      if (index < result.length) {
        // 逐步将 result 中的字符添加到 targetContent 中
        messages.value[messages.value.length - 1].content +=
          result.charAt(index);
        // 滚动到底部
        handleScrollTop();
        index++;
      } else {
        // 当所有字符都添加完毕后，清除定时器并解析 Promise
        clearInterval(timer);
        window.localStorage.setItem("messages", JSON.stringify(messages.value));
        resolve();
      }
    }, interval);
  });
};

/**
 * 从提供的 URL 中以流式方式获取数据并进行处理。
 * @param {string} url - 要获取数据的 URL。
 * @param {RequestInit} [options] - 传递给 fetch 请求的选项。
 * @returns {Promise<void>} - 在获取和处理完成时解析的 Promise。
 */
const fetchStream = async (
  url: string,
  options?: RequestInit,
): Promise<void> => {
  try {
    // 初始化加载状态
    loading.value = false;
    // 添加机器人消息
    addRobotMessage("");
    const response = await fetch(url, options);
    const reader = response.body!.getReader();
    try {
      let partialChunk = "";
      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        // 处理接收到的数据块
        partialChunk = await processChunk(partialChunk, value);
      }
      // 处理剩余的数据块
      await processRemainingChunk(partialChunk);
    } finally {
      // 释放 reader 锁并结束加载状态
      reader.releaseLock();
      loading.value = true;
    }
  } catch (error) {
    // 在 fetch 错误时更新消息
    messages.value[messages.value.length - 1].content =
      "服务器出现错误 , 请稍后重试😣";
    // 滚动到页面顶部
    handleScrollTop();
  }
};

/**
 * 处理从服务器接收到的数据块。
 * @param {string} partialChunk - 到目前为止累积的部分数据块。
 * @param {Uint8Array} value - 从服务器接收到的数据块。
 * @returns {Promise<string>} - 解析后的部分数据块的更新 Promise。
 */
const processChunk = async (
  partialChunk: string,
  value: Uint8Array,
): Promise<string> => {
  // 解码接收到的数据块
  const chunk = new TextDecoder().decode(value);
  // 将数据块添加到部分数据中
  partialChunk += chunk;
  // 将数据块拆分成 JSON 字符串数组
  const jsonStrings = partialChunk
    .split(/data: /)
    .filter((str) => str.trim() !== "");
  // 移除最后一个字符串并更新部分数据
  partialChunk = jsonStrings.pop() || "";
  // 逐个解析和处理 JSON 字符串
  for (const jsonString of jsonStrings) {
    await parseAndProcessJSON(jsonString);
  }
  return partialChunk;
};

/**
 * 处理剩余的数据块。
 * @param {string} partialChunk - 剩余的部分数据块。
 */
const processRemainingChunk = async (partialChunk: string) => {
  // 如果剩余部分不为空，则解析和处理该部分数据
  if (partialChunk.trim() !== "") {
    await parseAndProcessJSON(partialChunk);
  }
};

/**
 * 解析和处理 JSON 字符串。
 * @param {string} jsonString - 要解析和处理的 JSON 字符串。
 */
const parseAndProcessJSON = async (jsonString: string) => {
  try {
    // 解析 JSON 字符串
    const dataObject = JSON.parse(jsonString);
    // 如果成功解析，则进行后续处理
    if (dataObject) {
      const { result } = dataObject;
      // 执行打字效果
      await typeWriterEffect(result);
    }
  } catch (e) {
    // 捕获解析错误并记录
    console.error("解析 JSON 失败:", e);
  }
};

const accessToken: string =
  "24.0564dbe472f5e58618d194d6b0e71822.2592000.1756532241.282335-75794370";
const getMessage = (message: string) => {
  fetchStream(
    `https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-speed-128k?access_token=${accessToken}`,
    {
      method: "post",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ messages: messages.value, stream: true }),
    },
  );
};

onMounted(() => {
  const localStorageMessages = window.localStorage.getItem("messages");
  if (localStorageMessages) {
    messages.value = JSON.parse(localStorageMessages);
    handleScrollTop();
  }
});
</script>

<style lang="scss" scoped>
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}

.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

.chat-message::-webkit-scrollbar {
  width: 0;
}

.chat-container {
  padding-top: 68px;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #121212;
  border-radius: 12px;
  position: relative;

  .chat-header {
    position: absolute;
    left: 20px;
    top: 10px;
    font-size: 30px;
    color: transparent;
    background: linear-gradient(to right, #4285f4, #d96570);
    -webkit-background-clip: text;
    background-clip: text;
    font-weight: bold;
  }

  &::after {
    content: "";
    position: absolute;
    bottom: 105px;
    left: 0;
    width: 100%;
    height: 13%;
    background: -webkit-gradient(linear, left bottom, left top, from(#121212), color-stop(36%, rgba(18, 18, 18, 0.3)), to(rgba(18, 18, 18, 0)));
    background: linear-gradient(0deg, #121212 0%, rgba(18, 18, 18, 0.3) 36%, rgba(18, 18, 18, 0) 100%);
    z-index: 999;
    pointer-events: none;
  }

  .chat-message {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 90%;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    position: relative;
    padding: 10px 0;

    ul {
      max-width: 100%;
    }
  }

  .is-focus {
    border: 2px solid #dba91c !important;
  }

  .send-chat {
    height: 10%;
    width: 86%;
    background: #303030;
    margin-bottom: 30px;
    border-radius: 8px;
    padding: 10px;
    position: relative;
    transition: all 0.5s;
    border: 2px solid transparent;
    z-index: 1001;

    &:after {
      // content: "内容由AI生成，无法确保真实准确，仅供参考，基于灵一万物大模型";
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      bottom: -14%;
      font-size: 10px;
      color: #9d9ebc;
      width: 100%;
      text-align: center;
    }

    .send-btn {
      position: absolute;
      right: 8px;
      bottom: 8px;
      height: 30%;
      display: flex;
      justify-content: flex-end;

      button {
        width: 40px;
        height: 20px;
        border-radius: 15px;
        background-image: linear-gradient(to right, #fff025 0%, #e85832 100%);
        transition: all 0.5s;
        display: flex;
        justify-content: center;
        align-items: center;

        &:hover .iconfont {
          color: #4b2f2f;
        }

        .iconfont {
          color: #fff;
          transition: all 0.5s;
          font-size: 15px;
        }
      }
    }

    textarea {
      width: 100%;
      height: 80%;
      border: none;
      outline: none;
      resize: none;
      font-size: 14px;
      font-family: "宋体", serif;
      line-height: 1.5;
      background-color: #303030;
      color: #ececec;

      &::-webkit-scrollbar {
        width: 3px;
        /* 纵向滚动条*/
        height: 5px;
        /* 横向滚动条 */
        background-color: #fff;
      }

      /*定义滚动条轨道 内阴影*/
      &::-webkit-scrollbar-track {
        box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
        background-color: #fff;
        border-radius: 5px;
      }

      /*定义滑块 内阴影*/
      &::-webkit-scrollbar-thumb {
        box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
        background: #e5e5e5;
        border-radius: 10px;
      }
    }
  }
}
</style>
